package com.sandking.net;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtField;
import javassist.CtMethod;
import javassist.Modifier;
import javassist.NotFoundException;
import javassist.bytecode.CodeAttribute;
import javassist.bytecode.LocalVariableAttribute;
import javassist.bytecode.MethodInfo;

import com.sandking.net.imp.NetAnnotation;
import com.sandking.net.imp.NetReturn;
import com.sandking.net.pojo.NetClass;
import com.sandking.net.pojo.NetField;
import com.sandking.net.pojo.NetMethod;
import com.sandking.net.template.NTP;
import com.sandking.tools.SK_Map;
import com.sandking.tools.SK_Plus;
import com.sandking.tools.SK_String;

import freemarker.ext.beans.BeansWrapper;
import freemarker.template.Configuration;
import freemarker.template.DefaultObjectWrapper;
import freemarker.template.Template;

/**
 * @UserName : SandKing
 * @DataTime : 2013年11月27日 下午5:01:39
 * @Description ：生成器入口
 */
public class Net_Generate {
	private static Configuration cfg;

	public static Configuration getConfiguration() {
		if (cfg == null) {
			cfg = new Configuration();
			cfg.setClassForTemplateLoading(NTP.class, "");
			cfg.setObjectWrapper(new DefaultObjectWrapper());
		}
		return cfg;
	}

	public static Configuration getConfiguration(String path) {
		if (cfg == null) {
			try {
				cfg = new Configuration();
				cfg.setDirectoryForTemplateLoading(new File(path));
				cfg.setObjectWrapper(new BeansWrapper());
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		return cfg;
	}

	public static void run(NetConfig netCfg) throws Exception {

		examinePackage(netCfg);

		ClassPool pool = ClassPool.getDefault();
		CtClass cc = null;
		if (netCfg.getClientImp() != null) {
			cc = pool.get(netCfg.getClientImp().getName());
			writerClientImp(netCfg.getConfiguration(), netCfg, cc,netCfg.getClientImpPath(), netCfg.getClientImpName());
		}
		if (netCfg.getServerImp() != null) {
			cc = pool.get(netCfg.getServerImp().getName());
			writerServerImp(netCfg.getConfiguration(), netCfg, cc,netCfg.getServerImpPath(), netCfg.getServerImpName());
		}
		if (netCfg.getConstant() != null) {
			cc = pool.get(netCfg.getConstant().getName());
			writerConstant(netCfg.getConfiguration(), cc,
					netCfg.getConstantPath());
		}
		if (netCfg.getNetObject() != null) {
			cc = pool.get(netCfg.getNetObject().getName());
			writerNetObject(netCfg.getConfiguration(), cc,
					netCfg.getNetObjectPath());
		}
	}

	private static void writerClientImp(Configuration cfg, NetConfig netConfig,CtClass cc, String path, String className) throws Exception {
		writerImp(cfg, netConfig, cc, path, className, "clientImp.ftl");
	}

	private static void writerNetObject(Configuration cfg, CtClass cc,
			String path) throws Exception {
		List<CtClass> ctClassList = new ArrayList<CtClass>();
		
		CtClass[] superClazzs = cc.getSuperclass().getDeclaredClasses();
		for (CtClass ctClass : superClazzs) {
			ctClassList.add(ctClass);
		}
		// 取得内部类
		CtClass[] cls = cc.getDeclaredClasses();
		for (CtClass ctClass : cls) {
			ctClassList.add(ctClass);
		}
		NetClass netClass = null;
		for (CtClass ctClass : ctClassList) {
			CtField[] ctFields = ctClass.getDeclaredFields();
			List<NetField> netFields = new ArrayList<NetField>();
			Set<String> serverImports = new HashSet<String>();
			Set<String> clientImports = new HashSet<String>();
			Set<String> netObjImports = new HashSet<String>();
			
			netObjImports.add(HashMap.class.getName());
			netObjImports.add(Map.class.getName());
			
			Set<String> constantImports = new HashSet<String>();
			
			NetField netField = null;
			for (CtField ctField : ctFields) {
				if (ctField.getName().contains("$")) {
					continue;
				}
				String fieldName = ctField.getName();
				String dfieldName = SK_String.upperFirst(fieldName);
				String xfieldName = SK_String.lowerFirst(fieldName);
				String values = "";
				String type = ctField.getType().getSimpleName();
				NetAnnotation netAnnotation = (NetAnnotation) ctField
						.getAnnotation(NetAnnotation.class);
				String remark = netAnnotation.remark();
				netField = new NetField(type, fieldName, dfieldName,
						xfieldName, values, remark);
				netFields.add(netField);
			}

			NetAnnotation netAnnotation = (NetAnnotation) ctClass
					.getAnnotation(NetAnnotation.class);
			String remark = netAnnotation.remark();

			String className = ctClass.getSimpleName();
			className = getClassName(className);
			String dclassName = SK_String.upperFirst(className);
			String xclassName = SK_String.lowerFirst(className);
			String packageName = path.replace("src/", "").replace("/", ".");
			netClass = new NetClass(packageName, className, dclassName,
					xclassName, remark, null, netFields, serverImports,
					clientImports,netObjImports,constantImports);

			String filePath = SK_Plus.b(path, "/", className, ".java").e();
			File file = new File(filePath);
			Template template = cfg.getTemplate("netObject.ftl");
			fileWriter(template, file, netClass);
		}
	}

	private static void writerConstant(Configuration cfg, CtClass cc,
			String path) throws Exception {
		// 取得内部类
		CtClass[] cls = cc.getDeclaredClasses();
		NetClass netClass = null;
		for (CtClass ctClass : cls) {
			CtField[] ctFields = ctClass.getDeclaredFields();
			List<NetField> netFields = new ArrayList<NetField>();
			NetField netField = null;
			for (CtField ctField : ctFields) {
				if (ctField.getName().contains("$")) {
					continue;
				}
				String fieldName = ctField.getName();
				String dfieldName = SK_String.upperFirst(fieldName);
				String xfieldName = SK_String.lowerFirst(fieldName);
				String values = ctField.getConstantValue().toString();
				String type = ctField.getType().getSimpleName();
				NetAnnotation netAnnotation = (NetAnnotation) ctField
						.getAnnotation(NetAnnotation.class);
				String remark = netAnnotation.remark();
				netField = new NetField(type, fieldName, dfieldName,
						xfieldName, values, remark);
				netFields.add(netField);
			}

			NetAnnotation netAnnotation = (NetAnnotation) ctClass
					.getAnnotation(NetAnnotation.class);
			String remark = netAnnotation.remark();

			String className = ctClass.getSimpleName();
			className = getClassName(className);
			String dclassName = SK_String.upperFirst(className);
			String xclassName = SK_String.lowerFirst(className);
			String packageName = path.replace("src/", "").replace("/", ".");
			Set<String> serverImports = new HashSet<String>();
			Set<String> clientImports = new HashSet<String>();
			Set<String> netObjImports = new HashSet<String>();
			Set<String> constantImports = new HashSet<String>();
			
			netClass = new NetClass(packageName, className, dclassName,
					xclassName, remark, null, netFields, serverImports,
					clientImports,netObjImports,constantImports);

			String filePath = SK_Plus.b(path, "/", className, ".java").e();
			File file = new File(filePath);
			Template template = cfg.getTemplate("constant.ftl");
			fileWriter(template, file, netClass);
		}
	}

	private static void writerImp(Configuration cfg, NetConfig netConfig,CtClass cc, String path, String className,String templateName)throws Exception{
		NetAnnotation netAnnotation = (NetAnnotation) cc.getAnnotation(NetAnnotation.class);
		String remark = netAnnotation.remark();
		CtMethod[] methods = cc.getMethods();
		NetMethod netMethod = null;
		List<String> parameterTypes = new ArrayList<String>();
		List<String> parameterNames = new ArrayList<String>();
		List<String> returnTypes = new ArrayList<String>();
		List<String> returnNames = new ArrayList<String>();

		List<NetMethod> netMethods = new ArrayList<NetMethod>();
		Set<String> serverImports = new HashSet<String>();
		serverImports.add(SK_ChannelImp.class.getName());
		serverImports.add(Map.class.getName());
		serverImports.add(SK_Map.class.getName());
		serverImports.add(HashMap.class.getName());

		Set<String> clientImports = new HashSet<String>();
		clientImports.add(SK_ChannelImp.class.getName());
		clientImports.add(Map.class.getName());
		clientImports.add(SK_Map.class.getName());
		clientImports.add(HashMap.class.getName());
		
		Set<String> netObjImports = new HashSet<String>();
		Set<String> constantImports = new HashSet<String>();
		
		for (CtMethod ctMethod : methods) {
			netAnnotation = (NetAnnotation) ctMethod
					.getAnnotation(NetAnnotation.class);
			if (netAnnotation == null) {
				continue;
			}
			String[] parameterNameValues = getParameterName(ctMethod);
			parameterTypes = new ArrayList<String>();
			parameterNames = new ArrayList<String>();
			returnTypes = new ArrayList<String>();
			returnNames = new ArrayList<String>();
			// 方法注解
			remark = netAnnotation.remark();
			Object[][] parameterAnnotations = ctMethod.getParameterAnnotations();
			for (int i = 0; i < parameterAnnotations.length; i++) {
				// 有返回值的
				if (parameterAnnotations[i].length > 0) {
					NetReturn netReturn = (NetReturn) parameterAnnotations[i][0];
					CtClass parameterType = ctMethod.getParameterTypes()[i];
					serverImports.add(SK_Plus.b(
							netConfig.getNetObjectPath().replace("src/", "").replace("/", "."), ".",
							getClassName(parameterType.getSimpleName())).e());
					clientImports.add(SK_Plus.b(
							netConfig.getNetObjectPath().replace("src/", "").replace("/", "."), ".",
							getClassName(parameterType.getSimpleName())).e());
					returnTypes
							.add(getClassName(parameterType.getSimpleName()));
					returnNames.add(parameterNameValues[i]);
				} else {
					// 无返回值的
					CtClass parameterType = ctMethod.getParameterTypes()[i];
					parameterTypes.add(parameterType.getSimpleName());
					parameterNames.add(parameterNameValues[i]);
				}
			}
			//有返回状态的
			CtClass retrnType = ctMethod.getReturnType();
			String returnName = "void";
			if (retrnType!=null) {
				returnName = retrnType.getSimpleName();
				if (!"void".equals(returnName)) {
					serverImports.add(SK_Plus.b(
							netConfig.getNetObjectPath().replace("src/", "").replace("/", "."), ".",
							getClassName(returnName)).e());
					clientImports.add(SK_Plus.b(
							netConfig.getNetObjectPath().replace("src/", "").replace("/", "."), ".",
							getClassName(returnName)).e());
				}
			}
			netMethod = new NetMethod(remark, ctMethod.getName(),parameterTypes, parameterNames, returnTypes, returnNames,getClassName(returnName));
			netMethods.add(netMethod);
		}
		String packageName = path.replace("src/", "").replace("/", ".");
		String dclassName = SK_String.upperFirst(className);
		String xclassName = SK_String.lowerFirst(className);
		NetClass netClass = new NetClass(packageName, className, dclassName,
				xclassName, remark, netMethods, null, serverImports,
				clientImports,netObjImports,constantImports);
		String filePath = SK_Plus.b(path, "/", className, ".java").e();
		File file = new File(filePath);
		Template template = cfg.getTemplate(templateName);
		fileWriter(template, file, netClass);
	}
	
	private static void writerServerImp(Configuration cfg, NetConfig netConfig,CtClass cc, String path, String className) throws Exception {
		writerImp(cfg, netConfig, cc, path, className, "serverImp.ftl");
	}

	private static void fileWriter(Template template, File file, Object table) {
		try {
			FileWriter fileWriter = new FileWriter(file);
			template.process(table, fileWriter);
			fileWriter.flush();
			// 打印输出
			Writer writer = new OutputStreamWriter(System.out);
			template.process(table, writer);
			writer.flush();
			System.out.println();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	private static void examinePackage(NetConfig netConfig) {
		File file = new File(netConfig.getFilePath());
		if (!file.exists() || !file.isDirectory()) {
			file.mkdir();
		}

		file = new File(netConfig.getFilePath());
		if (!file.exists() || !file.isDirectory()) {
			file.mkdir();
		}

		file = new File(netConfig.getClientImpPath());
		if (!file.exists() || !file.isDirectory()) {
			file.mkdir();
		}

		file = new File(netConfig.getServerImpPath());
		if (!file.exists() || !file.isDirectory()) {
			file.mkdir();
		}

		file = new File(netConfig.getConstantPath());
		if (!file.exists() || !file.isDirectory()) {
			file.mkdir();
		}

		file = new File(netConfig.getNetObjectPath());
		if (!file.exists() || !file.isDirectory()) {
			file.mkdir();
		}
	}

	public static void completeInfo() {
		System.out.println("*********************************************");
		System.out.println("*                                           *");
		System.out.println("**********代码生成完成-请刷新项目！**********");
		System.out.println("*                                           *");
		System.out.println("*********************************************");
	}

	public static String getClassName(String className) {
		int index = className.indexOf("$") + 1;
		className = className.substring(index);
		return className;
	}

	public static String[] getParameterName(CtMethod cm)
			throws NotFoundException {
		MethodInfo methodInfo = cm.getMethodInfo();
		CodeAttribute codeAttribute = methodInfo.getCodeAttribute();
		LocalVariableAttribute attr = (LocalVariableAttribute) codeAttribute
				.getAttribute(LocalVariableAttribute.tag);
		if (attr == null) {
			// exception
		}
		String[] paramNames = new String[cm.getParameterTypes().length];
		int pos = Modifier.isStatic(cm.getModifiers()) ? 0 : 1;
		for (int i = 0; i < paramNames.length; i++)
			paramNames[i] = attr.variableName(i + pos);
		return paramNames;
	}
}
